package cn.qingyun.gis.modules.enterprise.util;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.enterprise.entity.Point;
import com.google.common.collect.Lists;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class GeoTool {

    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
    // π
    static double pi = 3.1415926535897932384626;
    // 长半轴
    static double a = 6378245.0;
    // 扁率
    static double ee = 0.00669342162296594323;

    public static Point getCenterPoint(String wkt) {
        try {
            LinkedList<Point> pointList = getPoints(wkt);
            int total = pointList.size();
            double X = 0, Y = 0, Z = 0;

            while (!pointList.isEmpty()) {
                Point point = pointList.pollFirst();
                if (point != null) {
                    double lat, lon, x, y, z;
                    lat = point.getLat().doubleValue() * Math.PI / 180;
                    lon = point.getLon().doubleValue() * Math.PI / 180;
                    x = Math.cos(lat) * Math.cos(lon);
                    y = Math.cos(lat) * Math.sin(lon);
                    z = Math.sin(lat);
                    X += x;
                    Y += y;
                    Z += z;
                }
            }


            X = X / total;
            Y = Y / total;
            Z = Z / total;
            double Lon = Math.atan2(Y, X);
            double Hyp = Math.sqrt(X * X + Y * Y);
            double Lat = Math.atan2(Z, Hyp);
            double longitude = Lon * 180 / Math.PI;
            double latitude = Lat * 180 / Math.PI;

            return Point.builder()
                    .lon(NumberUtil.toBigDecimal(longitude).setScale(6, RoundingMode.UP))
                    .lat(NumberUtil.toBigDecimal(latitude).setScale(6, RoundingMode.UP))
                    .build();
        } catch (Exception e) {
            return null;
        }
    }

    public static LinkedList<Point> getPoints(String wkt) {
        List<String> points = ReUtil.findAll(Pattern.compile("\\d{1,3}\\.\\d{3,} \\d{1,3}\\.\\d{3,}", Pattern.MULTILINE), wkt, 0);

        LinkedList<Point> result = Lists.newLinkedList();
        for (String point : points) {
            String[] split = point.split(" ");
            result.add(Point.builder()
                    .lon(NumberUtil.toBigDecimal(split[0]))
                    .lat(NumberUtil.toBigDecimal(split[1]))
                    .build());
        }
        return result;
    }

    /**
     * 将字符串多坐标点转换为坐标数组
     *
     * @param latLonString "116.616634,40.272665 116.644733,40.280371 116.636181,40.264352 116.616634,40.272665"
     * @return org.locationtech.jts.geom.Coordinate[]
     */
    public static Coordinate[] getCoordinateArray(String latLonString) {
        List<String> points = ReUtil.findAll(Pattern.compile("\\d{1,3}\\.\\d{3,} \\d{1,3}\\.\\d{3,}", Pattern.MULTILINE), latLonString, 0);


        Coordinate[] coordinateArray = new Coordinate[points.size() + 1];
        for (int i = 0; i < points.size(); i++) {
            String[] point = points.get(i).split(" ");
            Coordinate coordinate = new Coordinate(Double.valueOf(point[0]), Double.valueOf(point[1]));
            coordinateArray[i] = coordinate;
        }
        //保证首位地址相同
        String[] point = points.get(0).split(" ");
        Coordinate coordinateEnd = new Coordinate(Double.valueOf(point[0]), Double.valueOf(point[1]));
        coordinateArray[points.size()] = coordinateEnd;
        return coordinateArray;
    }


    /**
     * 判断坐标点是否在多坐标点组成的多边形面内
     *
     * @param coordinateArray
     * @param coordinate
     * @return boolean
     * within 判断是否在内部，边缘点返回false
     * intersects 判断是否相交，弥补 within边缘点缺陷，
     */
    public static boolean withinAndIntersects(Coordinate[] coordinateArray, Coordinate coordinate) {

        boolean result = false;
        //小于3个点无法组成多边形
        if (coordinateArray.length < 3) {
            return result;
        }

        //必须首尾坐标点相同组成闭合多边形
        if (!(coordinateArray[0].equals2D(coordinateArray[coordinateArray.length - 1]))) {
            return result;
        }

        GeometryFactory geometryFactory = new GeometryFactory();
        org.locationtech.jts.geom.Point point = geometryFactory.createPoint(coordinate);
        Polygon polygon = geometryFactory.createPolygon(coordinateArray);

        if (point.within(polygon) || point.intersects(polygon)) {
            result = true;
        }
        return result;
    }

    public static void main(String[] args) throws Exception {
//        GeometryFixer.fix()
//        Coordinate point = new Coordinate(new Double("118.295708"),new Double("35.197306"));
//        String kwt = "MULTIPOLYGON (((118.315553 35.228009,118.323524 35.225916,118.325617 35.218817,118.327744 35.218827,118.329632 35.206466,118.333788 35.208270,118.353842 35.204217,118.355390 35.192582,118.358873 35.193550,118.358659 35.189231,118.363957 35.186206,118.360842 35.179972,118.351506 35.181476,118.347447 35.169523,118.344291 35.171130,118.343641 35.167797,118.333150 35.165729,118.324775 35.148059,118.312595 35.144609,118.299646 35.150609,118.291194 35.169778,118.280795 35.179974,118.269372 35.181673,118.257021 35.174632,118.232885 35.180122,118.233703 35.192108,118.229702 35.197521,118.223125 35.199869,118.222761 35.209918,118.233006 35.211619,118.239100 35.219012,118.250415 35.219717,118.252697 35.226147,118.255396 35.222463,118.262259 35.220881,118.265762 35.223447,118.270467 35.220356,118.272894 35.225920,118.275188 35.224642,118.280961 35.221653,118.279426 35.218095,118.282769 35.216143,118.292099 35.218124,118.292606 35.216291,118.295276 35.221440,118.306652 35.220593,118.315553 35.228009)))";
//        Coordinate[] coordinateArray = getCoordinateArray(kwt);
//        System.out.println(kwt);
//        boolean res = withinAndIntersects(coordinateArray, point);
//        System.out.println(res);


        String filePath = "E:\\Program Files\\idea_project\\channel-system\\src\\main\\resources\\yantai.txt";

        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
            }

            String result = stringBuilder.toString();
//            System.out.println(result);
//            Coordinate[] coordinateArray = getCoordinateArray(result);
//            System.out.println(coordinateArray);
            List<String> polygon = getPolygonByWkt(result);
            FileOutputStream fileWriter = new FileOutputStream("E:\\Program Files\\idea_project\\channel-system\\src\\main\\resources\\request.txt", true);
            List<String> collect = polygon.stream().sorted(Comparator.comparingInt(String::length).reversed()).collect(Collectors.toList());
            fileWriter.write(collect.get(0).getBytes());
            fileWriter.flush();
            fileWriter.close();
//            String wkt = "MULTIPOLYGON (((117.045234073385 36.6857007013626,117.048291787826 36.6864722740522,117.0513935565 36.6874308900464,117.052421820807 36.687590101889,117.052822925453 36.6871961859335,117.053292049174 36.6860592855525,117.053300051289 36.6860462872513,117.053304052353 36.6860292881112,117.054923486463 36.6821086376327,117.054920485665 36.6820916369997,117.05493648999 36.6820776404734,117.055600671112 36.680470786296,117.055933762828 36.6793258602674,117.055992779121 36.6791258734064,117.057553035901 36.6792156047969,117.058114019525 36.6792384297984,117.061020514054 36.6794760845221,117.061585434405 36.6795478243693,117.063941775975 36.6797282472691,117.06639613624 36.679949380269,117.066850496391 36.6744694102137,117.066907315447 36.6741509341347,117.06504970425 36.6719784624437,117.071331798132 36.6718709119718,117.073444323212 36.6718587441145,117.077526600618 36.6718336666543,117.078326783689 36.6712619273764,117.080485981901 36.6723262764009,117.082412915042 36.673020404911,117.084647349197 36.6739782183192,117.097111174015 36.6765248493519,117.104526650227 36.6778913172189,117.109859415856 36.6791042421471,117.110332810443 36.6715026323281,117.110239466033 36.671215812138,117.111031831928 36.6684239980392,117.111152614802 36.6630877964646,117.11148882242 36.6594316270048,117.111519377794 36.6576571458345,117.113476725711 36.654151170404,117.114304635567 36.651461320643,117.114868993833 36.6487110296299,117.113641607395 36.6464216928283,117.11788022816 36.647923780317,117.120152574274 36.6482472825429,117.121999359454 36.6482304224873,117.12345519485 36.6481637723303,117.124847336053 36.648054992524,117.126344207563 36.6478438989282,117.127597811995 36.6475577160777,117.129754769003 36.6474828306685,117.133210479045 36.6467652389843,117.132249744731 36.6443327481045,117.132149285812 36.6421013869516,117.131800643442 36.6410538790273,117.13187250734 36.6397942232774,117.133398133744 36.6381069508737,117.133322293759 36.6365453220775,117.133060825134 36.6357596496173,117.133043530962 36.6346860659466,117.132620386257 36.6330172580771,117.132895173259 36.6320550874519,117.134294331118 36.6302431579746,117.134513880685 36.6292852775102,117.136063956136 36.6282676237076,117.137035202638 36.6270714308975,117.136293478147 36.6250692161326,117.140778155666 36.6243579680083,117.14287590131 36.6241926854505,117.145270113631 36.6218536840838,117.141904875442 36.6216710480632,117.141759891172 36.6195322075448,117.139451873145 36.6198035775594,117.139075398022 36.6175933019149,117.137953900565 36.616606379776,117.135670841832 36.6161587257102,117.134621054273 36.6139116118415,117.134450746741 36.6124913534747,117.13486455788 36.6112939902409,117.13738052438 36.6090352480436,117.137751717054 36.6074827131807,117.136712657401 36.6053242036819,117.137638670865 36.5981717846953,117.134807403497 36.5968708977478,117.133411614139 36.5954570039535,117.132041031924 36.5933240491997,117.13004069825 36.5930758411894,117.127994998673 36.592823518131,117.125820130864 36.5951447660271,117.122162180695 36.5952630104984,117.121405858045 36.5918152455939,117.120345688607 36.5906379350097,117.111923516682 36.5902031823094,117.11075534969 36.5908390781263,117.110419734483 36.5932276587328,117.108503890014 36.5956478243131,117.104577057871 36.5940436428636,117.099910224956 36.5947673781548,117.101312822597 36.5970216382821,117.099922365813 36.5995820850814,117.099996672839 36.6030289095604,117.098053390078 36.6038145891136,117.08962103734 36.6033717774005,117.08478800961 36.6027418808633,117.070615181881 36.6216204265665,117.069917959086 36.6224832475715,117.069683884238 36.6233521871978,117.069683883425 36.6248591859225,117.069526833122 36.6257271452307,117.069126706049 36.6264380430503,117.068334456194 36.6268318425479,117.066288819271 36.6270023317303,117.065557594444 36.6270661514002,117.063493968399 36.6274956491145,117.061029238181 36.6282860630612,117.059131690431 36.6282816236224,117.057860330928 36.6278833353214,117.054758480565 36.6255006537654,117.053565163177 36.6252593991073,117.052136790688 36.6254951000335,117.048965994573 36.6270674603898,117.046578425669 36.6274570034715,117.042369489836 36.6272892519288,117.039589920148 36.6276767941574,117.037837581264 36.6284625216441,117.034944056815 36.6308910995106,117.031963564883 36.6332577034533,117.030368322375 36.634243508222,117.028268025714 36.6351102694346,117.027766196823 36.6338246431494,117.027614262035 36.6329742696636,117.027044751203 36.632001278451,117.026816879483 36.6310292441777,117.022603171478 36.6317760004895,117.022489535857 36.6340523157348,117.02234832949 36.6366737088265,117.022104305599 36.6363556896622,117.021825278924 36.6357596683166,117.021354234218 36.6356026323465,117.014275722804 36.6354812198984,117.013368679062 36.6358731844358,117.013089666376 36.6362721740876,117.013046664167 36.6366671722223,117.013482682275 36.6380931865925,117.013560685022 36.6390441886221,117.013482680454 36.640151184703,117.013247668697 36.6414751749215,117.012968655517 36.6426641640134,117.012935653758 36.6430661625088,117.012941653963 36.6431211626635,117.013056658691 36.6433401664449,117.013389672864 36.6439471777866,117.013556680004 36.6445051834448,117.013615682462 36.6448221853664,117.013640683242 36.6452591859058,117.013605679717 36.6473951826098,117.013640680296 36.6485851828272,117.013808687021 36.6499571879714,117.013823686961 36.6508351877355,117.01376568332 36.6518221845822,117.013668677321 36.6534221793908,117.01356267146 36.6544611744313,117.013562671105 36.6548611740584,117.013741472795 36.655599975873,117.013470612392 36.6571258664682,117.013277124498 36.658074303026,117.013170649577 36.6592111557164,117.013048644024 36.6594891511675,117.012970640397 36.6598061481681,117.013170648698 36.6602011547892,117.013653670033 36.6608451718827,117.013673670962 36.66085617263,117.013688671635 36.6608921731652,117.015551767764 36.6633792501676,117.01595279112 36.6640532688426,117.016030795371 36.6646442721316,117.015987791805 36.6656952690136,117.015987787811 36.6702072647375,117.015796773781 36.672978252777,117.01598878391 36.6746812605309,117.015954781544 36.6750062585473,117.01605478757 36.6751232633749,117.016023785631 36.6751602618026,117.016546817664 36.6762062873516,117.017618889615 36.6771613450472,117.018412947781 36.6774803917807,117.021002164781 36.6777665662593,117.022353294224 36.6778266703162,117.024018468678 36.6781098104613,117.02472654791 36.6782048741066,117.025129594343 36.6782589114031,117.025538642486 36.6782769500804,117.025587648284 36.6783209547273,117.025690660586 36.6783349646075,117.02612771348 36.6783940070875,117.032151556591 36.6786446841263,117.031890513496 36.681302648745,117.032082543198 36.6816986724732,117.036336812742 36.6829618054659,117.038223704917 36.6807938326699,117.043040097702 36.6812194563577,117.042925156959 36.6835358106791,117.044999510533 36.686310930041,117.045234073385 36.6857007013626)))";
//            GeoTool geoTool = new GeoTool();
//            String s = geoTool.polygonExpansion(wkt);
//            FileOutputStream fileWriter = new FileOutputStream("E:\\Program Files\\idea_project\\channel-system\\src\\main\\resources\\request.txt", true);
//            fileWriter.write(s.getBytes());
//            fileWriter.flush();
//            fileWriter.close();
        }
    }

    /**
     * 根据wkt构造多边形搜索的参数 （37.964320|120.734194,37.965096|120.735591,37.966430|37.964320|120.734194）
     *
     * @param wkt
     * @return
     */
    public static List<String> getPolygonByWkt(String wkt) {
        List<String> coordinateList = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        WKTReader wktReader = new WKTReader();
        Geometry geometry = null;
        try {
            geometry = wktReader.read(wkt);
        } catch (ParseException e) {
            throw new CommonException("wkt数据格式错误");
        }
        if ("MultiPolygon".equals(geometry.getGeometryType())) {
            MultiPolygon multiPolygon = (MultiPolygon) geometry;
            for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
                sb.setLength(0);//清空StringBuilder
                Geometry polygon = multiPolygon.getGeometryN(i);
                Coordinate[] coordinates = polygon.getCoordinates();
                String lng = null;
                String lat = null;
                for (Coordinate coordinate : coordinates) {
                    lng = String.format("%.6f", coordinate.getX());
                    lat = String.format("%.6f", coordinate.getY());
                    sb.append(lng).append(",").append(lat).append("|");
                }
                sb.deleteCharAt(sb.length() - 1);
                coordinateList.add(sb.toString());
            }
        } else {
            Coordinate[] coordinates = geometry.getCoordinates();
            String lng = null;
            String lat = null;
            for (Coordinate coordinate : coordinates) {
                lng = String.format("%.6f", coordinate.getX());
                lat = String.format("%.6f", coordinate.getY());
                sb.append(lng).append(",").append(lat).append("|");
            }
            sb.deleteCharAt(sb.length() - 1);
            coordinateList.add(sb.toString());
        }
        return coordinateList;
    }

    /**
     * @description: 多边形扩展方法
     * @author skb
     * @date 2023/12/8 18:08
     */
    public static String polygonExpansion(String originalWkt) throws ParseException {
        // 创建WKT解析器和写入器
        WKTReader wktReader = new WKTReader();
        WKTWriter wktWriter = new WKTWriter();
        wktWriter.setFormatted(true);

        // 解析原始WKT数据为多边形对象
        Geometry originalGeometry = wktReader.read(originalWkt);

        // 定义距离参数（1公里）
        double distance = 0.009; // 单位：米

        // 进行多边形缓冲区操作来扩展每个点
        Geometry expandedGeometry = originalGeometry.buffer(distance);
        expandedGeometry.getGeometryType();

        // 将扩展后的几何对象转换为WKT格式数据
        String expandedWkt = wktWriter.write(expandedGeometry);

        return expandedWkt;
    }

    /**
     * 判断当前坐标是否在国内
     */
    public static boolean out_of_china(double lng, double lat) {
        if (lng < 72.004 || lng > 137.8347) {
            return true;
        } else if (lat < 0.8293 || lat > 55.8271) {
            return true;
        }
        return false;
    }

    /**
     * WGS84转GCJ02(火星坐标系)
     */
    public static double[] wgs84togcj02(double lng, double lat) {
        if (out_of_china(lng, lat)) {
            return new double[]{lng, lat};
        }
        double dlat = transformlat(lng - 105.0, lat - 35.0);
        double dlng = transformlng(lng - 105.0, lat - 35.0);
        double radlat = lat / 180.0 * pi;
        double magic = Math.sin(radlat);
        magic = 1 - ee * magic * magic;
        double sqrtmagic = Math.sqrt(magic);
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
        double mglat = lat + dlat;
        double mglng = lng + dlng;
        return new double[]{mglng, mglat};
    }

    public static double[] bd09togcj02(double bd_lon, double bd_lat) {
        double x = bd_lon - 0.0065;
        double y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gg_lng = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        return new double[]{gg_lng, gg_lat};
    }

    /**
     * 经度转换
     */
    public static double transformlat(double lng, double lat) {
        double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 纬度转换
     */
    public static double transformlng(double lng, double lat) {
        double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 利用java工具进行 84 -> 02
     */
    public static String convertWkts(String wkt) {
        // 进行经纬度正则匹配
        List<String> points = ReUtil.findAll(Pattern.compile("\\d{1,3}\\.\\d{3,} \\d{1,3}\\.\\d{3,}", Pattern.MULTILINE), wkt, 0);
        boolean success = true;
        for (String point : points) {
            String[] split = point.split(" ");
            if (split.length == 2) {
                double[] convertedLocation = wgs84togcj02(Double.valueOf(split[0]), Double.valueOf(split[1]));
                if (convertedLocation.length == 2) {
                    String formatted = String.format("%s %s", convertedLocation[0], convertedLocation[1]);
                    // log.debug(String.format("Location convert from [%s] to [%s]", point, formatted));
                    wkt = wkt.replaceAll(point, formatted);
                } else {
                    success = false;
                    break;
                }
            }
        }
        if (Boolean.FALSE.equals(success)) {
            return null;
        }

        return wkt;
    }


}
